﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Reflection;

namespace Vibz.Common.Channel
{
    public delegate Type ResolveType(string typeString);
    [XmlRoot(ElementName = "dp", IsNullable = true)]
    public class DataPacket
    {
        public const string XmlDeclarationText = "<?xml ";
        [XmlElement("dt")]
        public string DataType { get; set; }
        [XmlElement("h")]
        public DataPacketHeader Header { get; set; }
        public static List<KeyValuePair<DataPacketHeader, string>> Segregate(byte[] bytes)
        {
            String dataString = System.Text.Encoding.UTF8.GetString(bytes);
            string[] packetStrings = dataString.Split(new string[] { XmlDeclarationText }, StringSplitOptions.RemoveEmptyEntries);
            List<KeyValuePair<DataPacketHeader, string>> retValue = new List<KeyValuePair<DataPacketHeader, string>>();
            foreach (string packetString in packetStrings)
            {
                string ps = XmlDeclarationText + packetString;
                retValue.Add(new KeyValuePair<DataPacketHeader, string>(InspectData(ps), ps));
            }
            return retValue;
        }
        public static DataPacketHeader InspectData(byte[] bytes)
        {
            String dataString = System.Text.Encoding.UTF8.GetString(bytes);
            return InspectData(dataString);
        }
        public static DataPacketHeader InspectData(string dataString)
        {
            DataPacketHeader retValue;
            XmlSerializer serializer = new XmlSerializer(typeof(DataPacket));
            using (StringReader textReader = new StringReader(dataString))
            {
                using (XmlReader xmlReader = XmlReader.Create(textReader))
                {
                    DataPacket dp = ((DataPacket)serializer.Deserialize(xmlReader));
                    retValue = dp.Header;
                    retValue.DataType = dp.DataType;
                }
            }
            return retValue;
        }
        public static object DeserializeUnknown(string typeName, byte[] byteArray)
        {
            MethodInfo method = typeof(DataPacket).GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static);
            MethodInfo genericMethod = method.MakeGenericMethod(Type.GetType(typeName));
            return genericMethod.Invoke(null, new object[] { byteArray });
        }
        public static T Deserialize<T>(byte[] bytes)
        {
            DataPacket<T> retValue = null;
            XmlSerializer serializer = new XmlSerializer(typeof(DataPacket<T>));
            String dataString = System.Text.Encoding.UTF8.GetString(bytes);
            try
            {
                using (StringReader textReader = new StringReader(dataString))
                {
                    using (XmlReader xmlReader = XmlReader.Create(textReader))
                    {
                        retValue = (DataPacket<T>)serializer.Deserialize(xmlReader);
                    }
                }
            }
            catch (Exception exc)
            {
                Exception wExc= new Exception("DataPacket error: " + exc.Message);
                wExc.Data.Add("XMLText", dataString);
                wExc.Data.Add("Bytes", bytes.Length.ToString());
                throw wExc;

            }
            return retValue.Data;
        }
        public virtual byte[] Serialize()
        { return null; }
        public static byte[] Serialize(object obj)
        {
            try
            {
                XmlSerializer serializer = new XmlSerializer(obj.GetType());

                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Encoding = new UnicodeEncoding(false, false);
                settings.Indent = false;
                settings.OmitXmlDeclaration = false;
                byte[] retValue;
                using (StringWriter textWriter = new StringWriter())
                {
                    using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
                    {
                        serializer.Serialize(xmlWriter, obj);
                    }
                    retValue = System.Text.Encoding.ASCII.GetBytes(textWriter.ToString());
                }
                return retValue;
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.Message);
                throw;
            }
        }
    }

    [XmlRoot(ElementName = "dp", IsNullable = true)]
    public class DataPacket<T> : DataPacket
    {
        public DataPacket()
        { }
        public DataPacket(T data)
        {
            DataType = data.GetType().AssemblyQualifiedName;
            Data = data;
            Header = new DataPacketHeader();
        }
        public DataPacket(T data, DataPacketHeader header)
        {
            DataType = typeof(T).AssemblyQualifiedName;
            Data = data;
            Header = header;
        }
        [XmlElement("d")]
        public T Data { get; set; }
        public override string ToString()
        {
            return "[Packet]" + Data.ToString();
        }

        public override byte[] Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType());

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = new UnicodeEncoding(false, false);
            settings.Indent = false;
            settings.OmitXmlDeclaration = false;
            byte[] retValue;
            using (StringWriter textWriter = new StringWriter())
            {
                using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
                {
                    serializer.Serialize(xmlWriter, this);
                }
                retValue = System.Text.Encoding.ASCII.GetBytes(textWriter.ToString());
            }
            return retValue;
        }
    }
}
